package com.example.lottery.service.impl;

import com.example.lottery.common.config.DirectRabbitConfig;
import com.example.lottery.common.errorcode.ServiceErrorCodeConstants;
import com.example.lottery.common.exception.ServiceException;
import com.example.lottery.common.utils.JacksonUtil;
import com.example.lottery.common.utils.RedisUtil;
import com.example.lottery.common.utils.UUIDUtil;
import com.example.lottery.controller.param.DrawPrizeParam;
import com.example.lottery.controller.param.WinningRecordParam;
import com.example.lottery.dao.dataobject.*;
import com.example.lottery.dao.mapper.*;
import com.example.lottery.service.IDrawPrizeService;
import com.example.lottery.service.dto.WinningRecordDTO;
import com.example.lottery.service.enums.ActivityEnum;
import com.example.lottery.service.enums.ActivityPrizeEnum;
import com.example.lottery.service.enums.PrizeTiersEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class DrawPrizeService implements IDrawPrizeService {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityUserMapper activityUserMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private WinningRecordMapper winningRecordMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedisUtil redisUtil;

    private final String WINNING_RECORDS_PREFIX = "win:WINNING_RECORDS_";
    private final Long WINNING_RECORDS_TIMEOUT = 60 * 60 * 24 * 2L;
    /**
     * 发送消息给MQ
     * @param drawPrizeParam 消息体
     */
    @Override
    public void draw(DrawPrizeParam drawPrizeParam) {
        //  构造消息体
        Map<String,String>map=new HashMap<>();
        //唯一标识一个消息
        map.put("messageId", UUIDUtil.create());
        //DrawPrizeParam 请求参数
        map.put("messageData", JacksonUtil.writeValueAsString(drawPrizeParam));
        //指定交换机 KEY（确定队列） 消息体
        //从DirectRabbitConfig中拿
        //发送消息
        rabbitTemplate.convertAndSend(DirectRabbitConfig.EXCHANGE_NAME,DirectRabbitConfig.ROUTING,map);
    }

    /**
     * 校验中奖参数
     * @param drawPrizeParam 中奖消息体
     */
    @Override
    public boolean checkDrawPrizeParam(DrawPrizeParam drawPrizeParam) {
        //1-    活动Id是否存在、
        ActivityDO activityDO = activityMapper.selectByPrimaryKey(drawPrizeParam.getActivityId());
        if(activityDO==null){
            log.warn("活动不存在");
//            throw new ServiceException(ServiceErrorCodeConstants.DRAW_PRIZE_ACTIVITY_NOT_EXIST);
            return false;
        }
        //2-    活动已结束
        if(activityDO.getStatus().equalsIgnoreCase(ActivityEnum.COMPLETE.name())){
            log.warn("活动已结束");
//            throw new ServiceException(ServiceErrorCodeConstants.DRAW_PRIZE_ACTIVITY_STATUS_COMPLETE);
            return false;
        }


        //3-    中奖用户Id和活动Id构成的主键是否存在于活动用户表;
        List<Long>userIds = activityUserMapper.selectUserIdsByActivityId(drawPrizeParam.getActivityId());

        //获取中奖人的信息
        List<DrawPrizeParam.Winner> winnerList = drawPrizeParam.getWinnerList();
        //获奖用户Ids
        List<Long> winnerIds = winnerList.stream().map(winner -> {
            return winner.getUserId();
        }).collect(Collectors.toList());

       winnerIds.forEach(winnerId -> {
            if(!userIds.contains(winnerId)){
                //不包含，说明userId不存在
                log.warn("DrawPrizeParam Winner userId:{} 不存在",winnerId);
//                throw new ServiceException(ServiceErrorCodeConstants.DRAW_PRIZE_USER_NOT_EXIST);
            }
        });

        //4-    判断用户状态是否为INIT
        int count=activityUserMapper.selectUAStatus(winnerIds,drawPrizeParam.getActivityId(),ActivityEnum.COMPLETE.name());
            if(count!=0){
                log.warn("winnerList中有winnerId的状态为COMPLETE 已经抽过一次");
//                throw new ServiceException(ServiceErrorCodeConstants.DRAW_PRIZE_USER_STATUS_COMPLETE);
                return false;
            }

        //5-    奖品Id和活动Id构成的主键是否存在于活动用户表
        ActivityPrizeDO activityPrizeDO=activityPrizeMapper.selectPAInfo(drawPrizeParam.getPrizeId(),drawPrizeParam.getActivityId());
        if(activityPrizeDO==null){
            log.warn("奖品不存在 prizeId:{}",activityPrizeDO.getPrizeId());
//            throw new ServiceException(ServiceErrorCodeConstants.DRAW_PRIZE_PRIZE_NOT_EXIST);
            return false;
        }

        //6-    判断奖品状态是否为INIT
        if(activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeEnum.COMPLETE.name())){
            log.warn("奖品已被抽取 prizeId:{}",activityPrizeDO.getPrizeId());
//            throw new ServiceException(ServiceErrorCodeConstants.DRAW_PRIZE_PRIZE_STATUS_COMPLETE);
            return false;
        }
        //7-    中奖人数目和奖品份数是否相同
        if(activityPrizeDO.getPrizeAmount().longValue()!=(drawPrizeParam.getWinnerList().size())){
            log.warn("中奖人数目和奖品份数不同");
//            throw new ServiceException(ServiceErrorCodeConstants.DRAW_PRIZE_COUNT_ERROR);
            return false;
        }
        return true;
    }

    /**
     * 记录中奖信息
     * @param drawPrizeParam
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<WinningRecordDO> recordWinningInfo(DrawPrizeParam drawPrizeParam) {
        ActivityDO activityDO = activityMapper.selectByPrimaryKey(drawPrizeParam.getActivityId());
        if(activityDO==null){
            log.warn("活动不存在");
            throw new ServiceException(ServiceErrorCodeConstants.DRAW_PRIZE_ACTIVITY_NOT_EXIST);
        }
        //1- 构造中奖信息 由于用户可能有多个因此需要遍历创建WinningRecordDO对象
        List<WinningRecordDO> winningRecordDOS = drawPrizeParam.getWinnerList().stream().map(winner -> {
            WinningRecordDO winningRecordDO = new WinningRecordDO();
            winningRecordDO.setActivityId(drawPrizeParam.getActivityId());
            winningRecordDO.setActivityName(activityMapper.selectNameById(drawPrizeParam.getActivityId()));
            winningRecordDO.setPrizeId(drawPrizeParam.getPrizeId());
            winningRecordDO.setPrizeName(prizeMapper.selectNameById(drawPrizeParam.getPrizeId()));
            winningRecordDO.setWinningTime(drawPrizeParam.getWinningTime());
            //通过查奖品活动表获取
            winningRecordDO.setPrizeTier(activityPrizeMapper.selectPrizeTier(drawPrizeParam.getPrizeId(),drawPrizeParam.getActivityId()));
            //  遍历赋值
            winningRecordDO.setWinnerId(winner.getUserId());
            winningRecordDO.setWinnerName(winner.getUserName());
            winningRecordDO.setWinnerEmail(userMapper.selectEmailById(winner.getUserId()));
            winningRecordDO.setWinnerPhoneNumber(userMapper.selectPhoneById(winner.getUserId()));

            return winningRecordDO;
        }).collect(Collectors.toList());

        //2-    存入winning表中
        winningRecordMapper.insertList(winningRecordDOS);


        //3-    redis缓存中奖者记录
        // 1、缓存奖品维度中奖记录(WinningRecord_activityId_prizeId, winningRecordDOList（奖品维度的中奖名单）)
        cacheWinningRecords(drawPrizeParam.getActivityId() + "_" + drawPrizeParam.getPrizeId(),
                winningRecordDOS,
                WINNING_RECORDS_TIMEOUT);

        // 2、缓存活动维度中奖记录(WinningRecord_activityId, winningRecordDOList(活动维度的中奖名单))

        // 当活动已完成再去存放活动维度中奖记录
        if (activityDO.getStatus()
                .equalsIgnoreCase(ActivityPrizeEnum.COMPLETE.name())) {
            // 查询活动维度的全量中奖记录
            List<WinningRecordDO> allList = winningRecordMapper.selectByActivityId(drawPrizeParam.getActivityId());
            //缓存
            cacheWinningRecords(String.valueOf(drawPrizeParam.getActivityId()),
                    allList,
                    WINNING_RECORDS_TIMEOUT);
        }
        return winningRecordDOS;
    }

    /**
     * 回滚中奖信息
     * @param activityId 活动Id
     * @param prizeId 奖品Id
     */
    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        //删除中奖信息 活动Id不能为空
        if(activityId==null){
            log.warn("活动Id为空");
            return;
        }
        //1-    删除库里的信息 奖品Id为空就会删除该活动的所有中奖信息
        winningRecordMapper.deleteWinningRecordInfo(activityId, prizeId);

        //2-    删除redis中的信息 奖品、活动维度
        if(prizeId!=null){
            //删除奖品维度的redis
            deleteWinningRecords(activityId+"_"+prizeId);
        }
        //删除活动维度下的redis
        deleteWinningRecords(String.valueOf(activityId));
    }

    /**
     * 显示中奖记录 两个维度 奖品/活动
     * @param winningRecordParam
     * @return
     */
    @Override
    public List<WinningRecordDTO> showWinningRecords(WinningRecordParam winningRecordParam) {
        //1-    先判断活动Id是否合法
        if(activityMapper.selectByPrimaryKey(winningRecordParam.getActivityId())==null){
            log.warn("活动不存在");
            throw new ServiceException(ServiceErrorCodeConstants.DRAW_PRIZE_ACTIVITY_NOT_EXIST);
        }
        //2-    判断活动圈选的奖品Id不为空，判断是否合法
        if(winningRecordParam.getPrizeId()!=null&&
           activityPrizeMapper.selectPAInfo(winningRecordParam.getPrizeId(),winningRecordParam.getActivityId())==null){
            log.warn("奖品不存在");
            throw new ServiceException(ServiceErrorCodeConstants.DRAW_PRIZE_PRIZE_NOT_EXIST);
        }

        //3-    获取中奖信息
        //   奖品Id存在，以奖品维度获取中奖信息
        //   奖品Id不存在，以活动维度获取中奖信息
        //   判断redis中是否存在，不存在再从数据库获取放入redis
        String key= winningRecordParam.getPrizeId()==null?
                        String.valueOf(winningRecordParam.getActivityId())
                        :winningRecordParam.getActivityId()+"_"+winningRecordParam.getPrizeId();


        if(redisUtil.hasKey(WINNING_RECORDS_PREFIX+key)){
            //redis中存在 从redis中获取
        }else{
            //redis中不存在，从库中存放到redis
            List<WinningRecordDO> winningRecordDOS = winningRecordMapper.selectByActivityIdOrPrizeId(winningRecordParam.getPrizeId(),winningRecordParam.getActivityId());
            //存放到redis中
            cacheWinningRecords(key, winningRecordDOS,WINNING_RECORDS_TIMEOUT);
        }
        //从redis中获取
        List<WinningRecordDO> winningRecords = getWinningRecordsRedis(key);


        //4-    构造返回值
        return winningRecords.stream().map(winningRecordDO -> {
            WinningRecordDTO winningRecordDTO=new WinningRecordDTO();
            winningRecordDTO.setActivityId(winningRecordDO.getActivityId());
            winningRecordDTO.setActivityName(winningRecordDO.getActivityName());
            winningRecordDTO.setPrizeId(winningRecordDO.getPrizeId());
            winningRecordDTO.setPrizeName(winningRecordDO.getPrizeName());
            winningRecordDTO.setPrizeTier(PrizeTiersEnum.forName(winningRecordDO.getPrizeTier()));
            winningRecordDTO.setWinnerId(winningRecordDO.getWinnerId());
            winningRecordDTO.setWinnerName(winningRecordDO.getWinnerName());
            winningRecordDTO.setWinnerEmail(winningRecordDO.getWinnerEmail());
            winningRecordDTO.setWinnerPhoneNumber(winningRecordDO.getWinnerPhoneNumber());
            winningRecordDTO.setWinningTime(winningRecordDO.getWinningTime());
            return winningRecordDTO;
        }).collect(Collectors.toList());
    }



    /**
     * 从缓存中删除中奖记录
     *
     * @param key
     */
    private void deleteWinningRecords(String key) {
        try {
            if (redisUtil.hasKey(WINNING_RECORDS_PREFIX + key)) {
                // 存在再删除
                redisUtil.delete(WINNING_RECORDS_PREFIX + key);
            }
        } catch (Exception e) {
            log.error("删除中奖记录缓存异常，key:{}", key);
        }
    }



    /**
     * 缓存中奖记录
     *
     * @param key
     * @param winningRecordDOList
     * @param time
     */
    private void cacheWinningRecords(String key,
                                     List<WinningRecordDO> winningRecordDOList,
                                     Long time) {
        String str = "";
        try {
            if (!StringUtils.hasText(key)
                    || CollectionUtils.isEmpty(winningRecordDOList)) {
                log.warn("要缓存的内容为空！key:{}, value:{}", key, JacksonUtil.writeValueAsString(winningRecordDOList));
                return;
            }

            str = JacksonUtil.writeValueAsString(winningRecordDOList);
            redisUtil.set(WINNING_RECORDS_PREFIX + key,
                    str,
                    time);
        } catch (Exception e) {
            log.error("缓存中奖记录异常！key:{}, value:{}", WINNING_RECORDS_PREFIX + key, str);
        }

    }



    /**
     * 从缓存中获取中奖记录
     *
     * @param key
     * @return
     */
    private List<WinningRecordDO> getWinningRecordsRedis(String key) {
        try {
            if (!StringUtils.hasText(key)) {
                log.warn("要从缓存中查询中奖记录的key为空！");
                return Arrays.asList();
            }
            String str = redisUtil.get(WINNING_RECORDS_PREFIX + key);
            if (!StringUtils.hasText(str)) {
                return Arrays.asList();
            }

            return JacksonUtil.readListValue(str, WinningRecordDO.class);
        } catch (Exception e) {
            log.error("从缓存中查询中奖记录异常！key:{}", WINNING_RECORDS_PREFIX + key);
            return Arrays.asList();
        }
    }


}
